import Taro, { faceVerifyForPay } from '@tarojs/taro'
import { EventEmitter } from 'events'
import { MAX_LIMIT, CollectionNameError, CollectionIdError } from './define'

export const db = Taro.cloud.database()
export const _ = db.command
export const cloud = Taro.cloud

export interface DataStructure {
    [key: string]: any
}

export class DataBaseUtils extends EventEmitter {
    /**
     * 关键字匹配, 遍历属性进行搜索
     * @param key 搜索的值
     * @param retrievedKey 检索的键值名数组,空数组则全员检索
     */
    public KeywordMatching(key: string, retrievedKey: string[] = [], data: DataBaseUtils = this) {
        const keys = Object.keys(data)


        for (let i = 0; i < keys.length; i++) {
            const _key = keys[i];
            /** 判断是否需要关键值检索 */
            if (retrievedKey.length > 0) {
                /** 如果字段不配则跳到循环尾部 */
                if (!retrievedKey.includes(_key)) break;
            }
            // console.log('keys', _key, typeof this[_key]);
            if (typeof this[_key] === 'object') {
                if (Array.isArray(this[_key])) {
                    for (let arr = 0; arr < this[_key].length; arr++) {
                        const arrElement = this[_key][arr];
                        if (this.KeywordMatching(key, retrievedKey, arrElement)) return true
                    }
                } else {
                    if (this[_key] instanceof DataBaseUtils) {
                        if (this[_key].KeywordMatching(key, retrievedKey)) return true
                    }
                    else if (this.KeywordMatching(key, retrievedKey, this[_key])) return true
                }
            } else if (typeof this[_key] === 'number' || typeof this[_key] === 'string') {
                if (this[_key].toString().includes(key)) return true
            }
        }
        return false
    }
}

/** 数据库 */
export class DataBase extends DataBaseUtils {
    public static collectionName?: string;
    public static userOpenId: string;
    public static MAX_UPDATE = 100;
    public _id?: string;
    public _openid?: string;
    public otherInformation?: OtherInformation;
    public userid?: string;
    constructor(data: DataStructure = {}) {
        super()
        const { _id, _openid, otherInformation, userid } = data
        this._id = _id
        this._openid = _openid
        this.userid = userid
        this.otherInformation = new OtherInformation(otherInformation)

    }

    /** 获取 _id 的值, 如果_id为空会报错 */
    public get_id() {
        if (this._id) return this._id
        else throw new CollectionIdError();
    }

    /** 是否属于自己 */
    public async belongToOneself() {
        const _openid = this._openid
        const openid = await DataBase.GetUserOpenId()
        return _openid === openid
        // return true
    }

    /** 获取用户openid,全局只获取一次 */
    public static async GetUserOpenId() {
        if (this.userOpenId) return this.userOpenId
        const a = await Taro.cloud.callFunction({ name: "login", data: {} })
        if (a.result) this.userOpenId = a.result['openid']
        return this.userOpenId
    }


    public static async GetData(condition: Taro.DB.Query.IQueryCondition, offset: number = 0, fieldObject?: Taro.General.IAnyObject, orderByArray: OrderByArray[] = []) {
        if (!this.collectionName) throw new CollectionNameError()
        let a = db.collection(this.collectionName)
        for (let index = 0; index < orderByArray.length; index++) {
            const element = orderByArray[index];
            a = a.orderBy(element.fieldPath, element.string)
        }
        if (fieldObject) a = a.field(fieldObject)
        const r = await a.skip(offset).limit(MAX_LIMIT).where(condition).get()
        return r.data
    }

    /** 获取所有匹配的数据 */
    public static async GetAllData(condition: Taro.DB.Query.IQueryCondition, fieldObject?: Taro.General.IAnyObject): Promise<DataStructure[]> {
        if (!this.collectionName) throw new CollectionNameError()
        const count = (await db.collection(this.collectionName).where(condition).count()).total
        if (count === 0) return []
        console.log('count', count);

        // 计算需分几次取
        const batchTimes = Math.ceil(count / MAX_LIMIT)
        // 承载所有读操作的 promise 的数组
        const tasks = [] as Promise<Taro.DB.Query.IQueryResult>[]
        for (let i = 0; i < batchTimes; i++) {
            let a = db.collection(this.collectionName)
            if (fieldObject) a = a.field(fieldObject)
            const promise = a.skip(i * MAX_LIMIT).limit(MAX_LIMIT).where(condition).get()
            tasks.push(promise)
        }
        const g = (await Promise.all(tasks)).reduce((acc, cur) => {
            return {
                data: acc.data.concat(cur.data),
                errMsg: acc.errMsg,
            }
        })
        return g.data
    }
    /**
     * 数据过滤,移除key中前缀带下划线的属性
     * @param data 数据
     * @param recursion 是否递归
     */
    public static DataFilter(data: DataBase | DataStructure, recursion = false) {
        const data0 = {}
        const keys = Object.keys(data)
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            if (key.substr(0, 1) !== '_') {
                if (recursion && data[key] instanceof DataBase) {
                    data0[key] = this.DataFilter(data[key], recursion)
                } else {
                    data0[key] = data[key]
                }
            }
        }
        return data0
    }
    /**
     * 添加数据,默认移除_id和_openid等关键数据
     * @param data 数据
     * @param removekey 是否移除关键值 如:_id/_openid
     * @param recursion 是否递归
     */
    public static async AddData(data: DataBase, removekey: boolean = true, recursion = false): Promise<Taro.DB.Query.IAddResult> {
        if (!this.collectionName) throw new CollectionNameError()
        let data0 = removekey ? this.DataFilter(data, recursion) : data
        return await db.collection(this.collectionName).add({
            data: {
                ...data0,
            }
        })
    }
    /** 移除数据,自动移除_id和_openid等关键数据 */
    public static async RemoveData(_id: string): Promise<Taro.DB.Query.IRemoveResult> {
        if (!this.collectionName) throw new CollectionNameError()
        return await db.collection(this.collectionName).doc(_id).remove({})
    }
    /** 更新数据,自动移除_id和_openid等关键数据 */
    public static async UpdateData(data: DataBase): Promise<Taro.DB.Query.IUpdateResult> {
        if (!this.collectionName) throw new CollectionNameError()
        const _id = data.get_id()
        const data0 = this.DataFilter(data)
        return await db.collection(this.collectionName).doc(_id).update({
            data: {
                ...data0,
            }
        })
    }
    /** 使用Where更新数据,自动移除_开头的关键数据 */
    public static async UpdateDataInWhere(data: DataBase | DataStructure, condition: Taro.DB.Query.IQueryCondition): Promise<any> {
        if (!this.collectionName) throw new CollectionNameError()
        if (data._id) {
            try {
                const data0 = this.DataFilter(data)
                console.log(this.collectionName, '保存的数据量', Object.keys(data0).length, data0);
                const count = Math.ceil(Object.keys(data0).length / DataBase.MAX_UPDATE)
                console.log('分批次数', count);
                let re = undefined
                for (let i = 0; i < count; i++) {
                    const keys = Object.keys(data0).filter((_e, index) => {
                        return index >= i * DataBase.MAX_UPDATE && index < (i + 1) * DataBase.MAX_UPDATE
                    })
                    const data_ = {}
                    for (let i = 0; i < keys.length; i++) {
                        const key = keys[i];
                        data_[key] = data0[key]
                    }
                    console.log('批次', i, data_, Object.keys(data_).length);
                    re = await db.collection(this.collectionName).where(condition).update({
                        data: {
                            ...data_,
                            _id: undefined,
                            _openid: undefined,
                        }
                    })
                }
                return re
            } catch (error) {
                console.error('错误位置: datebase - UpdateDataInWhere', error);
                throw error;
            }
        } else throw new CollectionIdError();
    }
    /**
     * 添加数据,默认移除_id和_openid等关键数据
     * @param data 数据
     * @param removekey 是否移除关键值 如:_id/_openid
     * @param recursion 是否递归
     */
    public static async AddDataInWhere(data: DataBase | DataStructure, condition: Taro.DB.Query.IQueryCondition, removekey: boolean = true, recursion = false): Promise<Taro.DB.Query.IAddResult> {
        if (!this.collectionName) throw new CollectionNameError()
        let data0 = removekey ? this.DataFilter(data, recursion) : data
        return await db.collection(this.collectionName).where(condition).add({
            data: {
                ...data0,
            }
        })
    }
    /** 使用Where移除数据,自动移除_id和_openid等关键数据 */
    public static async RemoveDataInWhere(condition: Taro.DB.Query.IQueryCondition): Promise<Taro.DB.Query.IRemoveResult> {
        if (!this.collectionName) throw new CollectionNameError()
        return await db.collection(this.collectionName).where(condition).remove({})
    }
    /** 获取集合中指定记录的引用。方法接受一个 id 参数，指定需引用的记录的 _id。 */
    public static async GetDoc(_id: string) {
        if (!this.collectionName) throw new CollectionNameError()
        return await db.collection(this.collectionName).doc(_id).get({})
    }

    /** 获取集合中指定记录的引用。方法接受一个 id 参数，指定需引用的记录的 _id。 */
    public static async DocUpdate(_id: string, data: DataBase | DataStructure,) {
        if (!this.collectionName) throw new CollectionNameError()
        return await db.collection(this.collectionName).doc(_id).update({
            data: {
                ...data,
            }
        })
    }

    /**
     * 使用Where更新数据,自动移除_开头的关键数据
     * 与上面UpdateDataInWhere有一些不同
     * @param data 数据
     * @param condition 条件
     */
    public static async UpdateDataInWhere2(data: DataBase | DataStructure, condition: Taro.DB.Query.IQueryCondition): Promise<any> {
        if (!this.collectionName) throw new CollectionNameError()
        try {
            const data0 = this.DataFilter(data)
            const count = Math.ceil(Object.keys(data0).length / DataBase.MAX_UPDATE)
            let re = undefined
            for (let i = 0; i < count; i++) {
                const keys = Object.keys(data0).filter((_e, index) => {
                    return index >= i * DataBase.MAX_UPDATE && index < (i + 1) * DataBase.MAX_UPDATE
                })
                const data_ = {}
                for (let i = 0; i < keys.length; i++) {
                    const key = keys[i];
                    data_[key] = data0[key]
                }
                re = await db.collection(this.collectionName).where(condition).update({
                    data: {
                        ...data_
                    }
                })
            }
            return re
        } catch (error) {
            console.error('错误位置: datebase - UpdateDataInWhere2', error);
            throw error;
        }
    }
}

/** 其他信息 */
export class OtherInformation {
    public createDate: Date | Taro.DB.Database.ServerDate;
    constructor(data: DataStructure = {}) {
        if (data['createDate']) {
            this.createDate = new Date(data['createDate'])
        } else {
            this.createDate = db.serverDate()
        }
    }
}

/** 在数据库使用Get时获取的数据规范 */
export class GetDataSpecification<T extends DataBase> {
    public count: number = 0
    public data: T[] = []
    constructor(count: number, data: T[]) {
        this.count = count
        this.data = data
    }
    public getId(_id: string) {
        for (let i = 0; i < this.data.length; i++) {
            const e = this.data[i];
            if ('_id' in e) {
                if (e._id === _id) return e
            }
        }
        return null
    }
}